﻿using IOP.Net;
using IOP.Pulsar.Abstractions;
using IOP.Pulsar.Options;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Buffers;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace IOP.Pulsar.Server
{
    /// <summary>
    /// 原始嵌套字服务器
    /// </summary>
    public class RawSocketServer<TContext> : IHostedService
        where TContext : class
    {
        /// <summary>
        /// 报文长度
        /// </summary>
        private const int MinimumBufferSize = 1500;
        /// <summary>
        /// 日志
        /// </summary>
        private readonly ILogger<RawSocketServer<TContext>> _looger;
        /// <summary>
        /// 配置
        /// </summary>
        private readonly IConfiguration _configuration;
        /// <summary>
        /// 生命周期函数
        /// </summary>
        private readonly IHostLifetime _applicationLifetime;
        /// <summary>
        /// 服务
        /// </summary>
        private readonly IServiceProvider _service;
        /// <summary>
        /// 配置
        /// </summary>
        private readonly IOptions<RawSocketServerOption> _options;
        /// <summary>
        /// 服务器名
        /// </summary>
        private readonly string _serverName;

        /// <summary>
        /// 报文管线
        /// </summary>
        private readonly Pipe _bufferPipeline = new Pipe();

        /// <summary>
        /// 构造函数
        /// </summary>
        public RawSocketServer(ILogger<RawSocketServer<TContext>> logger,
            IConfiguration configuration,
            IHostLifetime applicationLifetime,
            IServiceProvider service,
            IOptions<RawSocketServerOption> options)
        {
            _looger = logger;
            _configuration = configuration;
            _applicationLifetime = applicationLifetime;
            _service = service;
            _options = options;
        }

        /// <summary>
        /// 启动服务
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task StartAsync(CancellationToken cancellationToken)
        {
            return Task.Run(async () =>
            {
                try
                {
                    //var ip = NetworkInterface.GetAllNetworkInterfaces()
                    //.Select(p => p.GetIPProperties())
                    //.SelectMany(p => p.UnicastAddresses)
                    //.Where(p => p.Address.AddressFamily == AddressFamily.InterNetwork && !IPAddress.IsLoopback(p.Address) && p.PrefixOrigin == PrefixOrigin.Dhcp)
                    //.FirstOrDefault()?.Address;

                    var listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
                    listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, 0));
                    await ProcessAsync(listenSocket);
                }
                catch(Exception e)
                {
                    _looger.LogError(e, "error");
                }
                finally
                {
                    await StopAsync(cancellationToken);
                }
            }, cancellationToken);
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task StopAsync(CancellationToken cancellationToken)
        {
            var cancel = new CancellationTokenSource();
            await _applicationLifetime.StopAsync(cancel.Token);
        }

        /// <summary>
        /// 处理函数
        /// </summary>
        /// <param name="rawSocket"></param>
        /// <returns></returns>
        private async Task ProcessAsync(Socket rawSocket)
        {
            try
            {
                SocketMonitor socket = new SocketMonitor(rawSocket);
                Task writing = FillPipeAsync(socket, _bufferPipeline.Writer);
                Task reading = ReadPipeAsync(socket, _bufferPipeline.Reader);
                await Task.WhenAll(reading, writing);
            }
            catch (Exception e)
            {
                _looger.LogError(e.Message + e.Source);
            }
        }

        /// <summary>
        /// 填充管线
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="writer"></param>
        /// <returns></returns>
        private async Task FillPipeAsync(SocketMonitor socket, PipeWriter writer)
        {
            while (true)
            {
                try
                {
                    Memory<byte> memory = writer.GetMemory(MinimumBufferSize);
                    int bytesRead = await socket.ConnectedSocket.ReceiveAsync(memory, SocketFlags.None);
                    if (bytesRead == 0) break;
                    writer.Advance(bytesRead);
                }
                catch (Exception e)
                {
                    _looger.LogError(e.Message + e.Source);
                }

                FlushResult result = await writer.FlushAsync();
                if (result.IsCompleted) break;
            }

            writer.Complete();
        }

        /// <summary>
        /// 读取管线
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="reader"></param>
        /// <returns></returns>
        private async Task ReadPipeAsync(SocketMonitor socket, PipeReader reader)
        {
            try
            {
                var entry = _service.GetRequiredService<IProductLineEntry<TContext>>();
                while (true)
                {
                    ReadResult result = await reader.ReadAsync();
                    ReadOnlySequence<byte> buffer = result.Buffer;
                    entry.EntryHandle(ref buffer, socket);
                    reader.AdvanceTo(buffer.Start, buffer.End);
                    if (result.IsCompleted) break;
                }
            }
            catch (Exception e)
            {
                _looger.LogError($"{_serverName} : {e.Message} \n {e.StackTrace}");
            }
            finally
            {
                reader.Complete();
            }
        }
    }
}
